﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows.Forms;

// Assemblies in the Resources folder
using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using ServiceStack;
using ServiceStack.Text;

namespace StackmobExportAWSImport
{
    public partial class AWSImporter : Form
    {
        private string JSONString { set; get; }

        #region Constructors

        private Form _parent { get; set; }

        public AWSImporter()
        {
            Initialize();
        }

        /// <summary>
        /// New constructor to handle child form AWS on close event
        /// </summary>
        /// <param name="parent"></param>
        public AWSImporter(Form parent)
        {
            Initialize();
            _parent = parent;
            Closed += (sender, args) => _parent.Show();
        }

        public void Initialize()
        {
            InitializeComponent();
            LoadSettings();
        }

        #endregion

        #region private functions

        private void LoadSettings()
        {
            // add the AWS regions to the dropdown to select
            var regions = RegionEndpoint.EnumerableAllRegions.ToList();
            cboRegion.DisplayMember = "DisplayName";
            cboRegion.ValueMember = "SystemName";
            cboRegion.DataSource = regions;
        }

        /// <summary>
        /// Get a list of all your tables from AWS
        /// </summary>
        private void GetAWSTables()
        {
            //Get a list of all your tables from AWS
            var amazonDynamoDBClient = GetAmazonDynamoDBClient();
            using (var client = amazonDynamoDBClient)
            {
                // Initial value for the first page of table names.
                string lastEvaluatedTableName = null;
                do
                {
                    // Create a request object to specify optional parameters.
                    var request = new ListTablesRequest
                    {
                        ExclusiveStartTableName = lastEvaluatedTableName
                    };

                    var response = client.ListTables(request);
                    var result = response.ListTablesResult;

                    // add to dropdown list
                    cboTables.DataSource = result.TableNames.ToList();

                    lastEvaluatedTableName = result.LastEvaluatedTableName;
                } while (lastEvaluatedTableName != null);
            }

            if (cboTables.Items.Count > 0)
                btnSubmit.Enabled = true;
        }

        /// <summary>
        /// Create Amazon DynamoDB Client
        /// </summary>
        /// <returns></returns>
        private AmazonDynamoDBClient GetAmazonDynamoDBClient()
        {
            // get the selected Region
            var endPointSelected = RegionEndpoint.EnumerableAllRegions.FirstOrDefault(endpoint => endpoint.SystemName == cboRegion.SelectedValue.ToString());

            if (string.IsNullOrWhiteSpace(txtSecret.Text) || string.IsNullOrWhiteSpace(txtAccess.Text))
                throw new Exception("Please enter the Secret and Access keys");

            // create and return Amazon client
            var amazonDbConfig = new AmazonDynamoDBConfig
            {
                ProxyCredentials = CredentialCache.DefaultCredentials,
                RegionEndpoint = endPointSelected,
            };

            return new AmazonDynamoDBClient(txtAccess.Text, txtSecret.Text, amazonDbConfig);
        }

        /// <summary>
        /// Return a collection of JsonObjects from a json string 
        /// </summary>
        /// <returns></returns>
        private IEnumerable<JsonObject> ReadJSONData()
        {
            if (string.IsNullOrWhiteSpace(JSONString))
                throw new Exception("No JSON to insert into the table. Please select a JSON file.");

            // convert string to json objects
            var jsonArray = JsonObject.ParseArray(JSONString.Trim());

            if (jsonArray.Count < 1)
                throw new Exception("Could not Parse the JSON format. Please check the json text format.");

            return jsonArray;
        }

        /// <summary>
        /// Inster data to AWS DB key/pair document (row) at a time
        /// </summary>
        /// <param name="jsonArray"></param>
        private void InsertAWSData(IEnumerable<JsonObject> jsonArray)
        {
            Table awsTable;
            txtIssues.Text = string.Empty;

            // get connection and Table to insert data into
            var amazonDynamoDBClient = GetAmazonDynamoDBClient();
            using (var client = amazonDynamoDBClient)
            {
                awsTable = Table.LoadTable(client, cboTables.SelectedValue.ToString()); // table name case sensitive
            }

            var i = 0;

            // iterate the json array (like rows)
            foreach (var jsonList in jsonArray)
            {
                try
                {
                    // build up the key/pair document (like columns)
                    var awsObject = new Document();
                    foreach (var json in jsonList)
                    {
                        awsObject[json.Key] = json.Value;
                    }

                    // insert into the amazon table
                    awsTable.PutItem(awsObject);
                }
                catch (Exception ex)
                {
                    txtIssues.Text += string.Format("Item {0} - Add Item Exception: " + ex.Message, i);
                    if (ex.Message.Contains("Object reference not set to an instance of an object"))
                    {
                        txtIssues.Text += " This may be caused by a Table, 'Column Name' or 'Hash Key' mismatch between the JSON Object and the AWS Table Columns";
                    }
                    txtIssues.Text += Environment.NewLine;
                }
                i++;
            }
        }

        /// <summary>
        /// Display the information just before insert into the table for easy reading
        /// </summary>
        /// <param name="jsonArray"></param>
        private void DisplayJSONResults(IEnumerable<JsonObject> jsonArray)
        {
            txtIssues.Text = string.Empty;
            var sb = new StringBuilder();
            txtResult.Text = string.Empty;

            var jsonLists = jsonArray as JsonObject[] ?? jsonArray.ToArray();
            
            // build up a string of the data to be inserted for easy reading
            foreach (var jsonList in jsonLists)
            {
                sb.Append(jsonList.Count() + " keys").Append(Environment.NewLine);
                foreach (var json in jsonList)
                {
                    sb.Append("- " + json.Key + ":" + json.Value).Append(Environment.NewLine);
                }
                sb.Append(Environment.NewLine);
            }

            // display the count of key/pairs (columns) and data
            txtResult.Text = string.Format("Processing {0} items in the file. ", jsonLists.Count());
            txtResult.Text += Environment.NewLine + Environment.NewLine + sb;
        }

        /// <summary>
        /// Convert a CSV file to json string format
        /// </summary>
        /// <param name="sr"></param>
        private void ProcessCSVData(StreamReader sr)
        {
            List<string> csvHeader = null;
            var csvLines = new List<List<string>>();
            string line;
            var i = 0;

            // read the file line by line
            while ((line = sr.ReadLine()) != null)
            {
                // spit the line
                var csvLine = line.Trim().Split(',').ToList();

                // get header data (first line)
                if (i == 0)
                {
                    // store header into only
                    csvHeader = csvLine;
                    i++;
                }
                else
                {
                    // get the values (other lines)
                    csvLines.Add(csvLine);
                }
            }

            // create a collection of json objects
            var jsonData = new JsonArrayObjects();
            if (csvHeader != null && csvLines.Count > 0)
            {
                foreach (var csvLine in csvLines)
                {
                    try
                    {
                        var jo = new JsonObject();
                        for (i = 0; i < csvLine.Count; i++)
                        {
                            // add the header and data to the object
                            jo.Add(csvHeader[i], csvLine[i]);
                        }
                        jsonData.Add(jo);
                    }
                    catch (Exception ex)
                    {
                        txtIssues.Text += string.Format("CSV to JSON Parse Exception (Line {0}): " + ex.Message + Environment.NewLine, csvLines.IndexOf(csvLine) + 1); // +1 to add the header line
                    }
                }
            }

            // Serialize back to string
            JSONString = jsonData.SerializeToString() ?? string.Empty;
        }

        #endregion

        #region Events

        private void btnTables_Click(object sender, EventArgs e)
        {
            try
            {
                GetAWSTables();
                txtIssues.Text = cboTables.Items.Count < 1 ? "No AWS tables to display with your settings. Please check the Key and Region settings" : string.Empty;
            }
            catch (AmazonClientException cex)
            {
                txtIssues.Text = "AWS Client Connection Exception: " + cex.Message;
            }
            catch (AmazonDynamoDBException dbex)
            {
                txtIssues.Text = "AWS DB Exception: " + dbex.Message;
            }
            catch (Exception ex)
            {
                txtIssues.Text = "Exception: " + ex.Message;
            }
        }

        private void launchStackmobExporterToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _parent.Show();
            Hide();
        }

        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            try
            {
                txtIssues.Text = string.Empty;

                openFileDialog.Filter = "Files| *.txt;*.csv;*.json|All Files (*.*)|*.*";

                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    txtFile.Text = openFileDialog.FileName;
                    
                    // read file being uploaded
                    using (var sr = new StreamReader(openFileDialog.FileName))
                    {
                        var extension = Path.GetExtension(openFileDialog.FileName);

                        // process the CSV or txt, json
                        if (extension != null && extension.ToLower() == ".csv")
                            ProcessCSVData(sr);
                        else
                            JSONString = sr.ReadToEnd().Trim();

                        sr.Close();
                    }
                }

                txtResult.Text = JSONString ?? string.Empty;
            }
            catch (Exception ex)
            {
                txtIssues.Text = "Parse Exception: " + ex.Message;
            }
        }

        private void btnSubmit_Click(object sender, EventArgs e)
        {
            IEnumerable<JsonObject> jsonArray = null;
            try
            {
                jsonArray = ReadJSONData();
            }
            catch (Exception ex)
            {
                txtIssues.Text = "JSON Parse Exception, please check JSON file format: " + ex.Message + Environment.NewLine;
            }

            try
            {
                if (jsonArray == null)
                {
                    throw new Exception("Null value on the JSON Array");
                }

                var enumerable = jsonArray as JsonObject[] ?? jsonArray.ToArray();
                DisplayJSONResults(enumerable);

                if (MessageBox.Show(
                    string.Format("Have you selected the correct table to import the data into ({0}) ?" + Environment.NewLine + Environment.NewLine + "Please make sure you have selected the correct table or columns may be appended to the table."
                                  + Environment.NewLine + Environment.NewLine + "The data to be imported is dispalyed in the 'Import Text' box.", cboTables.SelectedItem),
                    "Check Selected Table", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
                    return;

                InsertAWSData(enumerable);

                MessageBox.Show("All done!", "Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (AmazonClientException cex)
            {
                txtIssues.Text += "AWS Client Connection Exception: " + cex.Message;
            }
            catch (AmazonDynamoDBException dbex)
            {
                txtIssues.Text += "AWS DB Exception: " + dbex.Message;
            }
            catch (Exception ex)
            {
                txtIssues.Text += "AWS Insert Exception: " + ex.Message;
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            txtFile.Text = string.Empty;
            txtIssues.Text = string.Empty;
            txtResult.Text = string.Empty;
        }

        private void cboRegion_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                GetAWSTables();
            }
            catch { }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var readme = new AWSReadMe();
            readme.ShowDialog();
        }

        private void abountToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var about = new AboutBox();
            about.ShowDialog();
        }

        #endregion

        #region Sample Data Insert

        /// <summary>
        /// http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ItemCRUDDotNetHelperAPI.html
        /// </summary>
        public static void PutSampleUser()
        {
            return;

            Table userTable;
            using (var client = new AmazonDynamoDBClient())
            {
                userTable = Table.LoadTable(client, "user"); // case sensitive
            }

            var user = new Document();
            user["username"] = "AWS@gmail.com";
            user["account_status"] = "VERIFIED";
            user["account_type"] = "FREE";
            user["company_abn"] = "067787";
            user["company_acctname"] = "Bank of AWS";
            user["company_acctnumber"] = "93472";
            user["company_acn"] = "2302889";
            user["company_bsb"] = "06289";
            user["company_businessphone"] = "+6167777";
            user["company_mobilephone"] = "+61418788";
            user["company_name"] = "Inc";
            user["company_postcode"] = "3100";
            user["company_state"] = "New Stack Wales";
            user["company_streetaddress"] = "46 AWS Street";
            user["company_suburb"] = "AWSville";
            user["companyid"] = "qf97igu4mz3v";
            user["created_timestamp"] = 1386755538366;  // Name change
            user["emailaddress"] = "AWS@gmail.com";
            user["firstname"] = "Bob";
            user["invoice_terms_days"] = "90";
            user["last_modified_timestamp"] = 1387106174889; // Name change
            user["mail_mailbox"] = "aws.qf9ib7igu4mz3v@aws.com";
            user["mail_password"] = "fzt8u282uzri";
            user["quote_terms_days"] = "90";
            user["sm_owner"] = "user/aws@gmail.com";
            user["surname"] = "Build";
            
            userTable.PutItem(user);
        }

        #endregion
    }
}